package solveur;


import ilog.concert.IloException;
import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.concert.IloNumVarType;
import ilog.cplex.IloCplex;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;

import programme.Contrainte;
import programme.Programme;
import programme.Solution;
import programme.SousExpression;
import programme.TypeContrainte;
import programme.TypeVariable;
import programme.Variable;
import programme.VecteurVariables;

public class CPLEXInterface implements SolverInterface {

	public Solution resoudre(Programme p) throws Exception{
		Solution solution = null;
		
//		try {
			// Construction du modele
			IloCplex cplex = new IloCplex();

			HashMap<Variable, IloNumVar> variables = new HashMap<Variable, IloNumVar>();
			// Création des variables pour CPLEX
			for (VecteurVariables vec : p.getVecteursVariables().values()) {
				for (Variable var : vec.getVariables()) {
					if( var.getType() == TypeVariable.CONTINUE){
						variables.put(var, cplex.numVar(var.getBorneMin(), var.getBorneMax(), IloNumVarType.Float));
					}
					if( var.getType() == TypeVariable.DISCRET){
						variables.put(var, cplex.numVar(var.getBorneMin(), var.getBorneMax(), IloNumVarType.Int));
					}
					if( var.getType() == TypeVariable.BOOLEEN){
						variables.put(var, cplex.boolVar());
					}
					
				}
			}
			
			// Construction de la fonction objectif
			LinkedList<IloNumExpr> objExprs = new LinkedList<IloNumExpr>();
			for (SousExpression ssexpr : p.getObjectif().getFonction().getSsExpressions()) {
				IloNumExpr expr = cplex.constant(ssexpr.getCoef());
				for (Variable var : ssexpr.getVar()) {
					expr = cplex.prod(expr, variables.get(var));
				}
				objExprs.add(expr);
			}
			IloNumExpr objectif = cplex.sum(objExprs.toArray(new IloNumExpr[0]));
			System.out.println("CPLEX obj :" + objectif);
			if( p.getObjectif().isMin() ){
				cplex.addMinimize(objectif);
			}else{
				cplex.addMaximize(objectif);
			}
			
			// Construction des contraintes
			System.out.println("NB contraintes : " + p.getContraintes().size());
			for (Contrainte c : p.getContraintes()) {
				LinkedList<IloNumExpr> contrSsExprs = new LinkedList<IloNumExpr>();
				for (SousExpression ssexpr : c.getExpression().getSsExpressions()) {
					IloNumExpr expr = cplex.constant(ssexpr.getCoef());
					for (Variable var : ssexpr.getVar()) {
						expr = cplex.prod(expr, variables.get(var));
					}
					contrSsExprs.add(expr);
				}
				
				IloNumExpr contrainte = cplex.sum(contrSsExprs.toArray(new IloNumExpr[0])); 
				
				// Quel est le type de contrainte ?
				if( c.getType() == TypeContrainte.INF){
					cplex.addGe(c.getBorne(), contrainte);
				}else if ( c.getType() == TypeContrainte.SUP ){
					cplex.addLe(c.getBorne(), contrainte);
				}else if ( c.getType() == TypeContrainte.EGAL ){
					cplex.addEq(c.getBorne(), contrainte);
				}else{
					throw new Exception("Type de contrainte non supporté, utilisé uniquement = (EGAL), ≤ (INF), ≥ (SUP)");
				}
			}
			
			System.out.println("CPLEX : " + cplex);
			// On lance cplex
			if (cplex.solve()) {
				// On récupère les valeurs
				HashMap<Variable, Double> values = new HashMap<Variable, Double>();
				for (Variable var : variables.keySet()) {
					System.out.println("get " + variables.get(var));
					values.put(var,cplex.getValue(variables.get(var)));
				}
				
				solution = new Solution(new ArrayList<Variable>(variables.keySet()));
				solution.setValeurs(values);
				
				// On récupère la valeur optimal
				solution.setResultat(cplex.getObjValue());
			}
			
			System.out.println("Statut : " + cplex.getStatus());
			System.out.println("Solution : " + solution);
			cplex.end();
			
			
			return solution;
//		}
//		catch (IloException ex) {
//			System.out.println(" Erreur CPLEX : " + ex);
//			return null;
//		} catch (Exception e) {
//			e.printStackTrace();
//			return null;
//		}
		
	}

}
